package com.huang.security.handler;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.huang.constant.Common;
import com.huang.enums.UserCodeEnum;
import com.huang.resp.CommonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;

/**
 * 自定义鉴权管理
 *
 * @author han
 * @since 2022-01-16 14:49:16
 */
@Slf4j
@Component
public class DefaultAuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext authorizationContext) {
        log.info("开始自定义鉴权！");

        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        String path = request.getURI().getPath();

        String token = request.getHeaders().getFirst(Common.Token.TOKEN_HEADER_NAME);
        if (StrUtil.isBlank(token)) {
            return Mono.just(new AuthorizationDecision(false));
        }

        Object authorityList = redisTemplate.opsForHash().get(Common.SystemConfig.OAUTH_URLS, path);
        List<String> authorities = Collections.singletonList(null != authorityList ? authorityList.toString() : "");
        return authentication
                //判断是否认证成功
                .filter(Authentication::isAuthenticated)
                //获取认证后的全部权限
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                //如果权限包含则判断为true
                .any(roleId -> {
                    //超级管理员直接放行
                    log.info("当前用户的权限： 【{}】", roleId);
                    roleId = StrUtil.replace(roleId, Common.Authority.PREFIX, "");
                    log.info("截取后的权限： 【{}】", roleId);
                    if (StrUtil.equals(Common.SystemConfig.SUPER_ADMIN, roleId)) {
                        return true;
                    }
                    //其他必须要判断角色是否存在交集
                    return authorities.contains(roleId);
                })
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
    }

    @Override
    public Mono<Void> verify(Mono<Authentication> authentication, AuthorizationContext object) {
        return check(authentication, object)
                .filter(AuthorizationDecision::isGranted)
                .switchIfEmpty(Mono.defer(() -> {
                    String body = JSONObject.toJSONString(CommonResult.error(UserCodeEnum.PERMISSION_DENIED));
                    return Mono.error(new AccessDeniedException(body));
                })).flatMap(d -> Mono.empty());

    }
}
